package torres.ai;

import java.util.ArrayList;
import java.util.Arrays;

import torres.ability.AbilityManager;
import torres.ability.ActionAbility;
import torres.controller.Direction;
import torres.entity.Entity;
import torres.entity.NPC;
import torres.fileIO.BasicContainer;
import torres.fileIO.ClassContainer;
import torres.map.World;

/**
 * @author Torres
 * AI follows the Avatar entity if within a defined range. If not, roam randomly.
 *
 */
public class PassiveAI extends AI{
	private static final int MAXSPEED = 5;
	private static final int MAX = 5;
	private ArrayList<NPC> enemyList;
	private Direction randD;
	private int counter;
	private int sleep;
	private int[] speeds;

	/**
	 * Creates a new default PassiveAI
	 */
	public PassiveAI () {
		super();
		enemyList = new ArrayList<NPC>();
		super.range = 2;
		counter = MAX;
		sleep = 3;
		//speed = MAXSPEED;
		randD = generateDirection();
	}
	
	/* (non-Javadoc)
	 * @see torres.ai.AI#addEntity(torres.entity.Entity)
	 */
	public synchronized void addEntity( NPC enemy ) {
		enemyList.add(enemy);
		speeds = new int[enemyList.size()];
		Arrays.fill(speeds, MAXSPEED);
	}
	
	/* (non-Javadoc)
	 * @see torres.ai.AI#removeEntity(torres.entity.Entity)
	 */
	public synchronized void removeEntity( NPC enemy ) {
		enemyList.remove(enemy);
		speeds = new int[enemyList.size()];
		Arrays.fill(speeds, MAXSPEED);
	}
	
	/* (non-Javadoc)
	 * @see torres.ai.AI#runAll()
	 */
	public synchronized void runAll() {
		Thread t = new Thread(this);
		t.setName("PassiveAI" + t.getId());
		t.start();
	}
	
	/* (non-Javadoc)
	 * @see torres.ai.AI#run()
	 */
	public synchronized void run () {
		
		if(player == null) {
			player = World.getInstance().getPlayer();
		}
		
		int pX = player.getLocation().getX();
		int pY = player.getLocation().getY();

		for(int a = 0; a < enemyList.size(); ++a) {
			NPC enemy = enemyList.get(a);

			int eX = enemy.getLocation().getX();
			int eY = enemy.getLocation().getY();
			
			int dX = eX-pX;
			int dY = eY-pY;

			if(speeds[a] > 0) {
				if(Math.random() > 0.5)
					speeds[a]--;
			}
			else {
				if(enemy.isAsleep()) {
					ActionAbility failSleep = AbilityManager.getInstance().getActionAbility(203);
					failSleep.useAbility(enemy);
					if(Math.random() > 0.8) {
						enemy.setAwake();
					}
				}
				else if((dX*dX + dY*dY) <= (range+1)*(range+1) && player.isVisible() && enemy.isHostile()) {
					moveTowardsPlayer( enemy, dX, dY );
				}
				else {
					if(sleep-- > 0) {
						//Do nothing
					}
					else {
						if (sleep <= 0) {
							java.util.Random rand = new java.util.Random();
							int num = rand.nextInt(500);
							
							if (num == 12) {
								sleep = rand.nextInt(90) + 30;
							}
						}
						moveRandomly(enemy);
					}
				}
				speeds[a] = MAXSPEED;
			}
		}
	}
	
	/**
	 * Moves entity in a random direction.
	 * @param enemy Entity to move
	 */
	public void moveRandomly(Entity enemy) {
		if(counter <= 0) {
			randD = generateDirection();
			counter = MAX;
		}
		if(!enemy.move(randD)) {
			randD = generateDirection();
		}
		counter--;
	}
	
	/**
	 * Moves Entity towards enemy Entity. 
	 * @param enemy Entity to move towards
	 * @param deltaX Distance from enemy Entity in respect to X
	 * @param deltaY Distance from enemy Entity in respect to Y
	 */
	public void moveTowardsPlayer( NPC enemy, int deltaX, int deltaY ) {
		Direction dx = checkXDirection(deltaX, deltaY);
		Direction dy = checkYDirection(deltaX, deltaY);
		
		if (Math.abs(deltaX) <= 0 && Math.abs(deltaY) <= 0) {
			//Do nothing or attack etc
			return;
		}
		if(Math.abs(deltaX) >= Math.abs(deltaY)) {
			if(!enemy.move(dx)) {
				if(!enemy.move(dy)) {
					if(!enemy.move(checkXDirection(deltaX,0)))
						enemy.move(checkYDirection(0,deltaY));
				}
			}
		}
		else {
			if(!enemy.move(dy)) {
				if(!enemy.move(dx)) {
					if(!enemy.move(checkYDirection(0,deltaY)))
						enemy.move(checkXDirection(deltaX,0));
				}
			}
		}
	}
	
	/**
	 * Checks to direction to move in respect X.
	 * @param dx distance between Entity and enemy Entity in respect to X
	 * @param dy distance between Entity and enemy Entity in respect to Y
	 * @return Direction to move
	 */
	public Direction checkXDirection(int dx, int dy) {		
		if(dx > 0 && dy > 0 )
			return Direction.NORTHWEST;
		else if(dx > 0 && dy < 0)
			return Direction.SOUTHWEST;
		else if(dx > 0 )
			return Direction.WEST;
		else 
			return Direction.EAST;
	}
	
	/**
	 * Checks to direction to move in respect Y.
	 * @param dx distance between Entity and enemy Entity in respect to X
	 * @param dy distance between Entity and enemy Entity in respect to Y
	 * @return Direction to move
	 */
	public Direction checkYDirection(int dx, int dy) {
		if(dy > 0 && dx < 0)
			return Direction.NORTHEAST;
		else if(dy < 0 && dx < 0)
			return Direction.SOUTHEAST;
		else if(dy > 0 )
			return Direction.NORTH;
		else 
			return Direction.SOUTH;
	}
	
	/**
	 * Creates the next Direction of the AI.
	 * @return Direction of next movement
	 */
	public Direction generateDirection() {
		java.util.Random ran = new java.util.Random();
		int selector = ran.nextInt(8);

		switch(selector) {
			case 0:
				return Direction.EAST;
			case 1:
				return Direction.NORTH;
			case 2:
				return Direction.NORTHEAST;
			case 3:
				return Direction.NORTHWEST;
			case 4:
				return Direction.SOUTH;
			case 5:
				return Direction.SOUTHEAST;
			case 6:
				return Direction.SOUTHWEST;
			case 7:
				return Direction.WEST;
		}
		return Direction.NORTH;
	}

	/* (non-Javadoc)
	 * @see torres.ai.AI#save()
	 */
	public ClassContainer save() {
		ClassContainer curClass = super.save();
		curClass.setName("PassiveAI");
		ClassContainer curStruct = new ClassContainer("EnemyList");
		for(NPC entity: enemyList) {
			curStruct.addProperty(entity.save());
		}
		curClass.addProperty(curStruct);
		curClass.addProperty(new BasicContainer("randD", randD));
		curClass.addProperty(new BasicContainer("counter", counter));
		curClass.addProperty(new BasicContainer("sleep", sleep));
		ClassContainer speedsCont = new ClassContainer("Speeds");
		if(speeds!=null) {
			for(int i=0; i<speeds.length; i++) {
				speedsCont.addProperty(new BasicContainer("speed", speeds[i]));
			}
		}
		curClass.addProperty(speedsCont);
		return curClass;
	}

	/* (non-Javadoc)
	 * @see torres.ai.AI#load(torres.fileIO.ClassContainer)
	 */
	public void load(ClassContainer property) {
		super.load(property);
		enemyList.clear();
		ClassContainer enemyListCont = (ClassContainer)property.getProperty("EnemyList");
		ClassContainer curEnemyCont;
		int i=0;
		while((curEnemyCont = (ClassContainer)enemyListCont.getProperty("NPC", i))!=null) {
			NPC curEnemy = new NPC();
			curEnemy.load(curEnemyCont);
			enemyList.add(curEnemy);
			i++;
		}
		i=0;
		speeds = new int[enemyList.size()];
		Arrays.fill(speeds, MAXSPEED);
		//ClassContainer speedsCont = (ClassContainer)enemyListCont.getProperty("speeds");
		/*if(speedsCont!=null) {
			BasicContainer curSpeedCont;
			while((curSpeedCont = (BasicContainer)speedsCont.getProperty("speed", i))!=null) {
				speeds[i] = Integer.parseInt((String)curSpeedCont.getValue());
				i++;
			}
		}*/
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		String s = "Passive AI: ";
		for(NPC e : enemyList)
			s += (e.toString() + ", ");
		return s;
	}

}